import CryptoJS from 'crypto-js';
import request from '@/utils/hsj/http/index'
import {handleUploadFormData} from '@/common/utils/handleUploadData'
export function autoUpload(file: File, params: {bizType: string, [key: string]: any}, data: {updateProgress ?: (progress: number) => void}) {
  return new Promise((resolve, reject) => {
    // console.log('autoUpload', file, params, file.size > 1 * 1024 * 1024);
    // 对文件大小判断大于10M的文件进行分片上传
    // if (file.size > 1 * 1024 * 1024) {
    // }
    chunkUpload(file, params, data).then(res => {
      resolve(res)
    }).catch(error => {
      reject(error)
    })
    // return single(file, params)

  })
}



// 单文件上传
// function single(file: File, params: Record<string, any>) {
//   return new Promise((resolve, inject) => {
//     const formData = new FormData()
//     formData.append('file', file)
//     request.base('post', '/basic/file/upload', formData, {params}).then((res) => {
//       resolve(res)
//     }).catch((error) => {
//       inject(error)
//     })
//   })
// }
/**
 * 合并多个Blob对象
 * @param {Blob[]} blobs 要合并的Blob数组
 * @param {string} [type] 新Blob的MIME类型
 * @returns {Blob} 合并后的新Blob
 */
function mergeBlobs(blobs: Blob[], type: string = 'text/plain'): Blob {
  return new Blob(blobs, { type: type || blobs[0]?.type });
}
// 分片上传
function chunkUpload(file: File, params: {bizType: string, [key: string]: any},data: {updateProgress ?: (progress: number) => void}) {
  return new Promise(async(resolve, inject) => {
    // 文件切片原始数据
    const fileChunkList = createFileChunk(file)
    // console.log('fileChunkList', fileChunkList);
    
    // 切片chunk + 文件名的Blob数据
    const md5List = fileChunkList.map((item) => mergeBlobs([item.chunk, new Blob([file.name], { type: 'text/plain' })]))
    // console.log('md5List', md5List);
    
    // 批量计算md5 => hash
    const hashList = await calculateBlobHashes(md5List, 'SHA-1')
    // console.log('hashList', hashList);

    const fileChunkListWithHash = fileChunkList.map((item: any, index) => {
      item.hash = hashList[index].hash
      item.status = hashList[index].status
      return item
    })
    
    const fileUniqueBlob = mergeBlobs(hashList.map((item) => new Blob([item.hash], { type: 'text/plain' })))
    const fileUnique = await calculateBlobHashes([fileUniqueBlob], 'SHA-1')
    // ! 初始化接口
    const [initResData, error] = await $to(partUploadInit({
      ...params,
      fileUnique: fileUnique[0].hash,
      fileName: file.name,
      partCount: fileChunkList.length,
      chunks: hashList.map((item) => ({
        md5:item.hash,
        partNumber: item.index + 1
      }))
    }))
    if(error) {inject('文件上传初始化失败！'); return}
    // ! 创建分片上传接口的任务
    const partUploadTasks = initResData.data.chunks.map((item: any) => {
        const data = {
          id: initResData.data.id,
          uploadId: initResData.data.uploadId,
          partCount:fileChunkListWithHash.length,
          currentPartNumber: item.partNumber,
          chunks: [item],
          file: {file: fileChunkListWithHash.find((fileChunkItem) => fileChunkItem.hash === item.md5).chunk}
        }
        const formDta = handleUploadFormData(data)
        return () => partUpload(formDta)
    })
    
    const res = await executeWithConcurrency(partUploadTasks, 5, data.updateProgress)
    const bool = res.every((item: any) => item.status === 'fulfilled' && item.result.code === 200)
    // console.log('bool',bool);
    
    if(!bool)  {inject('文件分片上传失败！'); return}
    // ! 合并接口
    partMerge({
      id: initResData.data.id,
      uploadId: initResData.data.uploadId
    }).then(res => {
      resolve(res)
    })
  })
}


function partUploadInit(data: {
  bizType: string; fileUnique?: string; fileName: string; partCount: number; chunks: {md5: string;partNumber: number}[]}) {
  return request.post('/basic/file/partUploadInit', data)
}
function partUpload(data: FormData) {
  return request.post('/oss/part/partUpload', data)
}
function partMerge(data: {id: string; uploadId: string;}) {
  return request.post('/oss/part/merge', data)
}


/**
 * 批量计算多个Blob的哈希值
 * @param blobs Blob对象数组
 * @param algorithm 哈希算法 (默认为 'SHA-256')
 * @param concurrency 并发数 (默认为5)
 * @returns Promise<Record<number, string>> 返回索引到哈希值的映射对象
 */
async function calculateBlobHashes(
  blobs: Blob[],
  algorithm: 'SHA-1' | 'SHA-256' | 'SHA-512' | 'MD5' = 'SHA-1',
  concurrency: number = 5
): Promise<{index: number, hash: string, status: 'fulfilled' | 'rejected'}[]> {
  // 并发控制执行

  // 创建任务列表
  const tasks = blobs.map((blob, index) => async () => ({
    index,
    hash: await calculateBlobHash(blob, algorithm)
  }));
  

  // 执行并发任务
  const results = await executeWithConcurrency(tasks, concurrency);
  // console.log('results', results);
  

  // 转换为索引映射对象
  return results.map((item) => ({index: item.index, hash: item.result?.hash ?? '', status: item.status}))
}



/**
* file 需要切片的文件
* chunkSize 每个切片大小
*/
function createFileChunk(file: File, chunkSize = 5 * 1024 * 1024) {
  // 创建数组保留切片数组
  const fileChunkList = []
  let cur = 0 // 切片当前数据
  let i = 0 // 下标
  while (cur < file.size) {
    fileChunkList.push({
      chunk: new File([file.slice(cur, cur + chunkSize)], file.name, {
        type: file.type,
        lastModified: file.lastModified
      }),
      name: file.name,
      index: i,
      percentage: 0
    })
    cur += chunkSize
    i++
  }
  // 返回最后切片的数组
  return fileChunkList
}



/**
 * 计算 Blob 内容的哈希值
 * @param blob 要计算哈希的 Blob 对象
 * @param algorithm 哈希算法 (默认为 'SHA-256')
 * @returns Promise<string> 哈希值的十六进制字符串
 */
async function calculateBlobHash(
  blob: Blob,
  algorithm: 'SHA-1' | 'SHA-256' | 'SHA-512' | 'MD5' = 'SHA-1'
): Promise<string> {
  
  // 1. 将 Blob 转换为 ArrayBuffer
  const arrayBuffer = await blob.arrayBuffer();
  
  // 2. 使用 Web Crypto API 计算哈希
  // const hashBuffer = await crypto.subtle.digest(algorithm, arrayBuffer);
  const hashBuffer = await cryptoJsDigest(algorithm, arrayBuffer);
  // console.log('hashBuffer', hashBuffer);
  // 3. 将哈希值转换为十六进制字符串
  const hashArray = Array.from(new Uint8Array(hashBuffer));
  const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
  // console.log('hashHex', hashHex);
  
  return hashHex;
}


/**
 * 带并发控制的异步任务执行器
 * @param tasks 异步任务数组（每个任务是无参函数，返回Promise）
 * @param maxConcurrency 最大并发数（默认3）
 * @returns Promise<Array<{index: number, result: T, status: 'fulfilled' | 'rejected'}>> 
 */
async function executeWithConcurrency<T>(
  tasks: (() => Promise<T>)[],
  maxConcurrency: number = 3,
  updateProgress?: (progress: number) => void
): Promise<Array<{index: number, result?: T, error?: Error, status: 'fulfilled' | 'rejected'}>> {
  const results: Array<{index: number, result?: T, error?: Error, status: 'fulfilled' | 'rejected'}> = [];
  const executing = new Set<Promise<void>>();
  let currentIndex = 0;
  let num = 0

  // 执行单个任务并处理结果
  const executeTask = async (index: number) => {
    try {
      const result = await tasks[index]();
      updateProgress && updateProgress( Math.floor((num + 1) * 100 / tasks.length || 0))
      num++
      results.push({ index, result, status: 'fulfilled' });
    } catch (error) {
      results.push({ index, error: error as Error, status: 'rejected' });
    }
  };

  // 执行器主循环
  while (currentIndex < tasks.length) {
    // 填充执行队列
    while (executing.size < maxConcurrency && currentIndex < tasks.length) {
      const taskPromise = executeTask(currentIndex)
        .finally(() => executing.delete(taskPromise)); // 确保任务完成后从集合中移除
      executing.add(taskPromise);
      currentIndex++;
    }

    // 等待至少一个任务完成
    if (executing.size > 0) {
      await Promise.race(executing);
    }
  }

  // 等待所有剩余任务完成
  await Promise.all(executing);

  // 按原始索引顺序返回结果
  return results.sort((a, b) => a.index - b.index);
}



/**
 * 使用crypto-js模拟crypto.subtle.digest
 * @param algorithm 支持的算法：'SHA-1' | 'SHA-256' | 'SHA-512' | 'MD5'
 * @param buffer 要哈希的ArrayBuffer
 * @returns Promise<ArrayBuffer> 哈希结果
 */
async function cryptoJsDigest(
  algorithm: 'SHA-1' | 'SHA-256' | 'SHA-512' | 'MD5',
  buffer: ArrayBuffer
): Promise<ArrayBuffer> {
  // 转换ArrayBuffer为CryptoJS格式
  const wordArray = arrayBufferToWordArray(buffer);
  
  // 选择算法
  let hash;
  switch (algorithm) {
    case 'SHA-1':
      hash = CryptoJS.SHA1(wordArray);
      break;
    case 'SHA-256':
      hash = CryptoJS.SHA256(wordArray);
      break;
    case 'SHA-512':
      hash = CryptoJS.SHA512(wordArray);
      break;
    case 'MD5':
      hash = CryptoJS.MD5(wordArray);
      break;
    default:
      throw new Error(`Unsupported algorithm: ${algorithm}`);
  }

  // 转换回ArrayBuffer
  const hex = hash.toString(CryptoJS.enc.Hex);
  return hexToArrayBuffer(hex);
}
// 工具函数
function arrayBufferToWordArray(arrayBuffer: ArrayBuffer): CryptoJS.lib.WordArray {
  const u8 = new Uint8Array(arrayBuffer);
  const words: number[] = [];
  for (let i = 0; i < u8.length; i++) {
    words[i >>> 2] |= (u8[i] & 0xff) << (24 - (i % 4) * 8);
  }
  return CryptoJS.lib.WordArray.create(words, u8.length);
}

function hexToArrayBuffer(hex: string): ArrayBuffer {
  const matches = hex.match(/.{1,2}/g) || [];
  const u8 = new Uint8Array(matches.map(byte => parseInt(byte, 16)));
  return u8.buffer;
}
